package main

import (
	"fmt"
	"sync"
	"time"
)

/*
1、不同goroutine之间如何通讯
(1)全局变量加锁同步
(2)channel
2、使用全局变量加锁同步改进程序
(1)因为没有对全局变量m加锁，因此会出现资源争夺问题，代码会出现错误，提示concurrent map writes
(2)解决方案：加入互斥锁
(3)我们的数的阶乘很大，结果会越界，可以将求阶乘改成sum+=uint64(i)
*/
var (
	myMap = make(map[int]int, 10)
	//声明一个全局的互斥锁
	//lock是一个全局的互斥锁
	//sync是包：synchornized同步
	//Mutex：是互斥
	/*
		type Mutex struct {
		// 包含隐藏或非导出字段
		}
		Mutex是一个互斥锁，可以创建为其他结构体的字段；零值为解锁状态。
		Mutex类型的锁和线程无关，可以由不同的线程加锁和解锁。
	*/
	lock sync.Mutex //声明了一个结构体实例
)

//test函数就是计算n!，将这个结果放入到map中
func test(n int) {
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}
	//这里将res放入到myMap
	//加锁 func (m *Mutex) Lock()
	//Lock方法锁住m，如果m已经加锁，则阻塞直到m解锁。
	lock.Lock()
	myMap[n] = res //concurrent map writes?
	//解锁 func (m *Mutex) Unlock()
	//Unlock方法解锁m，如果m未加锁会导致运行时错误。锁和线程无关，可以由不同的线程加锁和解锁。
	lock.Unlock()
}
func main() {
	//开启多个协程
	for i := 1; i <= 20; i++ {
		go test(i)
	}
	//休眠几秒
	time.Sleep(time.Second)
	//输出结果
	/*
		这里为什么需要加互斥锁？
		按理说1秒数上面的协程都应该执行完，后面就不应该出现资源竞争的问题了
		但是在实际运行中，还是可能会出现问题（运行时增加-race参数，确实会发现有资源竞争问题）
		因为我们程序从设计上可以知道1秒就执行完所有协程，但是主线程并不知道，因此底层可能仍然出现资源争夺，因此加入互斥锁即可解决问题
	*/
	lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	lock.Unlock()
}
